import { PayoutStatus, PaymentChannel } from '@prisma/client';
import { prisma } from '../db/index';
import { Logger, AppError, ErrorCode } from '../utils/logger';
import { paymentService, CreatePayoutOrderRequest as PaymentCreatePayoutOrderRequest, PaymentResponse, PaymentNotification } from './paymentService';
import bcrypt from 'bcryptjs';

// Create payment order request parameters
export interface CreatePayoutOrderRequest {
  uid: string;
  amount: number;
  paymentChannel: PaymentChannel;
  paymentMethod: 'BankTransfer' | 'qrPay';
  createdByUserId?: number;
  createdByName: string;
  receiverName: string; // Receiver name
  receiverBankCard: string; // Receiver bank card number
  bankCode?: string; // Bank code
  remark?: string; // Payment remark
}

// Query payment orders request parameters
export interface QueryPayoutOrdersRequest {
  orderId?: string;
  uid?: string;
  status?: PayoutStatus;
  createdByUserId?: number;
  createdByName?: string;
  confirmedByUserId?: number;
  page?: number;
  limit?: number;
}

// Confirm payment order request parameters
export interface ConfirmPayoutOrderRequest {
  orderId: string;
  payoutPassword: string;
  confirmedByUserId: number;
}

// User statistics data
export interface PayoutUserStats {
  uid: string;
  totalAmount: number;
  totalCount: number;
  completedAmount: number;
  completedCount: number;
  pendingAmount: number;
  pendingCount: number;
  failedAmount: number;
  failedCount: number;
}

export class PayoutService {
  /**
   * Generate payment order ID
   */
  generateOrderId(channel: PaymentChannel): string {
    const prefix = channel === PaymentChannel.TOPPAY ? 'PO_TP' : 'PO_P6';
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(2, 8).toUpperCase();
    return `${prefix}${timestamp}${random}`;
  }

  /**
   * Create payment order (internal creation, pending confirmation status)
   */
  async createPayoutOrder(request: CreatePayoutOrderRequest): Promise<any> {
    Logger.info('Starting to create payment order', {
      uid: request.uid,
      amount: request.amount,
      paymentChannel: request.paymentChannel
    });

    try {
      // Generate order ID
      const orderId = this.generateOrderId(request.paymentChannel);

      // Create payment order record
      const payoutRecord = await prisma.payoutRecord.create({
        data: {
          uid: request.uid,
          amount: request.amount,
          status: PayoutStatus.PENDING_CONFIRM, // Initial status is pending confirmation
          orderId: orderId,
          paymentChannel: request.paymentChannel,
          paymentMethod: request.paymentMethod,
          createdByUserId: request.createdByUserId,
          createdByName: request.createdByName,
          receiverName: request.receiverName,
          receiverBankCard: request.receiverBankCard,
          bankCode: request.bankCode,
          remark: request.remark,
        },
        include: {
          createdByUser: {
            select: {
              id: true,
              username: true,
              email: true,
            }
          }
        }
      });

      Logger.info('Payment order created successfully', {
        orderId: payoutRecord.orderId,
        id: payoutRecord.id
      });

      return payoutRecord;
    } catch (error) {
      Logger.error('Failed to create payment order', error, {
        uid: request.uid
      });
      throw new AppError('Failed to create payment order', ErrorCode.PAYOUT_CREATE_ERROR);
    }
  }

  /**
   * Confirm payment order (admin second confirmation)
   */
  async confirmPayoutOrder(request: ConfirmPayoutOrderRequest): Promise<any> {
    Logger.info('Starting to confirm payment order', {
      orderId: request.orderId,
      confirmedByUserId: request.confirmedByUserId
    });

    try {
      // Find payment order
      const payoutRecord = await prisma.payoutRecord.findUnique({
        where: { orderId: request.orderId },
      });

      if (!payoutRecord) {
        throw new AppError('Payment order not found', ErrorCode.ORDER_NOT_FOUND);
      }

      if (payoutRecord.status !== PayoutStatus.PENDING_CONFIRM) {
        throw new AppError('Order status is incorrect, cannot confirm', ErrorCode.INVALID_ORDER_STATUS);
      }

      // Validate admin secondary password
      const adminUser = await prisma.user.findUnique({
        where: { id: request.confirmedByUserId },
      });

      if (!adminUser || !adminUser.payoutPassword) {
        throw new AppError('Admin has not set payment password', ErrorCode.PAYOUT_PASSWORD_NOT_SET);
      }

      const passwordValid = await bcrypt.compare(request.payoutPassword, adminUser.payoutPassword);
      if (!passwordValid) {
        throw new AppError('Incorrect payment password', ErrorCode.INVALID_PAYOUT_PASSWORD);
      }

      // Update order status to confirmed
      const updatedRecord = await prisma.payoutRecord.update({
        where: { orderId: request.orderId },
        data: {
          status: PayoutStatus.CONFIRMED,
          confirmedByUserId: request.confirmedByUserId,
          confirmedAt: new Date(),
        },
        include: {
          createdByUser: {
            select: {
              id: true,
              username: true,
              email: true,
            }
          },
          confirmedByUser: {
            select: {
              id: true,
              username: true,
              email: true,
            }
          }
        }
      });

      Logger.info('Payment order confirmed successfully', {
        orderId: request.orderId,
        confirmedByUserId: request.confirmedByUserId
      });

      return updatedRecord;
    } catch (error) {
      Logger.error('Failed to confirm payment order', error, {
        orderId: request.orderId
      });
      throw error;
    }
  }

  /**
   * Execute payment (call payment interface)
   */
  async executePayment(orderId: string): Promise<PaymentResponse> {
    Logger.info('Starting to execute payment', { orderId });

    try {
      // Find payment order
      const payoutRecord = await prisma.payoutRecord.findUnique({
        where: { orderId },
      });

      if (!payoutRecord) {
        throw new AppError('Payment order not found', ErrorCode.ORDER_NOT_FOUND);
      }

      if (payoutRecord.status !== PayoutStatus.CONFIRMED) {
        throw new AppError('Order status is incorrect, cannot execute payment', ErrorCode.INVALID_ORDER_STATUS);
      }

      // Update status to processing
      await prisma.payoutRecord.update({
        where: { orderId },
        data: { status: PayoutStatus.PROCESSING },
      });

      // Build payment request parameters (for withdrawal/payment to user)
      Logger.info('从数据库读取的代付订单信息', {
        orderId: payoutRecord.orderId,
        bankCode: payoutRecord.bankCode,
        receiverName: payoutRecord.receiverName,
        receiverBankCard: payoutRecord.receiverBankCard
      });

      const paymentRequest: PaymentCreatePayoutOrderRequest = {
        amount: Number(payoutRecord.amount),
        paymentChannel: payoutRecord.paymentChannel,
        paymentMethod: payoutRecord.paymentMethod as 'BankTransfer' | 'qrPay',
        orderId: payoutRecord.orderId,
        receiverName: payoutRecord.receiverName,
        receiverBankCard: payoutRecord.receiverBankCard,
        bankCode: payoutRecord.bankCode || '',
        accountName: payoutRecord.receiverName,
        customerEmail: `${payoutRecord.uid}@system.local`, // Use UID to generate default email
        customerPhone: '0000000000', // Default phone number
        remark: payoutRecord.remark || undefined,
      };

      // Call payment service to execute withdrawal
      const paymentResponse = await paymentService.createPayoutOrder(paymentRequest);

      // Update order record
      const finalStatus = paymentResponse.success ? PayoutStatus.COMPLETED : PayoutStatus.FAILED;
      await prisma.payoutRecord.update({
        where: { orderId },
        data: {
          status: finalStatus,
          platOrderNum: paymentResponse.platOrderNum,
          paymentResponse: JSON.stringify(paymentResponse.rawResponse),
        },
      });

      Logger.info('Payment execution completed', {
        orderId,
        success: paymentResponse.success,
        status: finalStatus
      });

      return paymentResponse;
    } catch (error) {
      // Update order status to failed
      await prisma.payoutRecord.update({
        where: { orderId },
        data: { status: PayoutStatus.FAILED },
      }).catch(() => {});

      Logger.error('Failed to execute payment', error, { orderId });
      throw error;
    }
  }

  /**
   * Query payment order list
   */
  async queryPayoutOrders(request: QueryPayoutOrdersRequest): Promise<any> {
    const { page = 1, limit = 20, ...filters } = request;
    const skip = (page - 1) * limit;

    const where: any = {};
    
    if (filters.orderId) {
      where.orderId = { contains: filters.orderId };
    }
    if (filters.uid) {
      where.uid = { contains: filters.uid };
    }
    if (filters.status) {
      where.status = filters.status;
    }
    if (filters.createdByUserId) {
      where.createdByUserId = filters.createdByUserId;
    }
    if (filters.createdByName) {
      where.createdByName = { contains: filters.createdByName };
    }
    if (filters.confirmedByUserId) {
      where.confirmedByUserId = filters.confirmedByUserId;
    }

    try {
      const [records, total] = await Promise.all([
        prisma.payoutRecord.findMany({
          where,
          skip,
          take: limit,
          orderBy: { createdAt: 'desc' },
          include: {
            createdByUser: {
              select: {
                id: true,
                username: true,
                email: true,
              }
            },
            confirmedByUser: {
              select: {
                id: true,
                username: true,
                email: true,
              }
            }
          }
        }),
        prisma.payoutRecord.count({ where })
      ]);

      return {
        records,
        pagination: {
          page,
          limit,
          total,
          pages: Math.ceil(total / limit)
        }
      };
    } catch (error) {
      Logger.error('Failed to query payment orders', error, filters);
      throw new AppError('Failed to query payment orders', ErrorCode.PAYOUT_QUERY_ERROR);
    }
  }

  /**
   * Get payment record by order ID
   */
  async getPayoutRecordByOrderId(orderId: string): Promise<any> {
    try {
      const record = await prisma.payoutRecord.findUnique({
        where: { orderId },
        include: {
          createdByUser: {
            select: {
              id: true,
              username: true,
              email: true,
            }
          },
          confirmedByUser: {
            select: {
              id: true,
              username: true,
              email: true,
            }
          }
        }
      });

      return record;
    } catch (error) {
      Logger.error('Failed to get payment record', error, { orderId });
      throw new AppError('Failed to get payment record', ErrorCode.PAYOUT_GET_ERROR);
    }
  }

  /**
   * Cancel payment order
   */
  async cancelPayoutOrder(orderId: string, canceledByUserId: number): Promise<any> {
    Logger.info('Starting to cancel payment order', { orderId, canceledByUserId });

    try {
      const payoutRecord = await prisma.payoutRecord.findUnique({
        where: { orderId },
      });

      if (!payoutRecord) {
        throw new AppError('Payment order not found', ErrorCode.ORDER_NOT_FOUND);
      }

      // Only orders with pending confirmation and confirmed status can be cancelled
      if (payoutRecord.status !== PayoutStatus.PENDING_CONFIRM && 
          payoutRecord.status !== PayoutStatus.CONFIRMED) {
        throw new AppError('Order status is incorrect, cannot cancel', ErrorCode.INVALID_ORDER_STATUS);
      }

      const updatedRecord = await prisma.payoutRecord.update({
        where: { orderId },
        data: {
          status: PayoutStatus.CANCELLED,
          // Can record the user who performed the cancellation here
        },
        include: {
          createdByUser: {
            select: {
              id: true,
              username: true,
              email: true,
            }
          },
          confirmedByUser: {
            select: {
              id: true,
              username: true,
              email: true,
            }
          }
        }
      });

      Logger.info('Payment order cancelled successfully', { orderId });
      return updatedRecord;
    } catch (error) {
      Logger.error('Failed to cancel payment order', error, { orderId });
      throw error;
    }
  }

  /**
   * Get user payment statistics
   */
  async getUserPayoutStats(uid: string): Promise<PayoutUserStats> {
    try {
      // Get statistics of all user payment records
      const stats = await prisma.payoutRecord.aggregate({
        where: { uid },
        _sum: { amount: true },
        _count: { id: true },
      });

      const completedStats = await prisma.payoutRecord.aggregate({
        where: { uid, status: PayoutStatus.COMPLETED },
        _sum: { amount: true },
        _count: { id: true },
      });

      const pendingStats = await prisma.payoutRecord.aggregate({
        where: { 
          uid, 
          status: { 
            in: [PayoutStatus.PENDING, PayoutStatus.PENDING_CONFIRM, PayoutStatus.CONFIRMED, PayoutStatus.PROCESSING] 
          } 
        },
        _sum: { amount: true },
        _count: { id: true },
      });

      const failedStats = await prisma.payoutRecord.aggregate({
        where: { uid, status: PayoutStatus.FAILED },
        _sum: { amount: true },
        _count: { id: true },
      });

      return {
        uid,
        totalAmount: Number(stats._sum?.amount || 0),
        totalCount: stats._count?.id || 0,
        completedAmount: Number(completedStats._sum?.amount || 0),
        completedCount: completedStats._count?.id || 0,
        pendingAmount: Number(pendingStats._sum?.amount || 0),
        pendingCount: pendingStats._count?.id || 0,
        failedAmount: Number(failedStats._sum?.amount || 0),
        failedCount: failedStats._count?.id || 0,
      };
    } catch (error) {
      Logger.error('Get user payment statistics失败', error, { uid });
      throw error;
    }
  }

  /**
   * Set or update admin payment password
   */
  async setPayoutPassword(userId: number, newPassword: string, currentPassword?: string): Promise<void> {
    Logger.info('Setting payment password', { userId });

    try {
      const user = await prisma.user.findUnique({
        where: { id: userId },
      });

      if (!user) {
        throw new AppError('User not found', ErrorCode.USER_NOT_FOUND);
      }

      if (user.role !== 'ADMIN' && user.role !== 'SUB_ADMIN') {
        throw new AppError('只有管理员可以Setting payment password', ErrorCode.PERMISSION_DENIED);
      }

      // If payment password already exists, need to verify current password
      if (user.payoutPassword && currentPassword) {
        const passwordValid = await bcrypt.compare(currentPassword, user.payoutPassword);
        if (!passwordValid) {
          throw new AppError('Current payment password is incorrect', ErrorCode.INVALID_CURRENT_PASSWORD);
        }
      }

      // Encrypt new password
      const hashedPassword = await bcrypt.hash(newPassword, 12);

      await prisma.user.update({
        where: { id: userId },
        data: { payoutPassword: hashedPassword },
      });

      Logger.info('Payment password set successfully', { userId });
    } catch (error) {
      Logger.error('Setting payment password失败', error, { userId });
      throw error;
    }
  }

  /**
   * Handle payout notification from payment channels
   */
  async handlePayoutNotification(channel: PaymentChannel, data: any): Promise<void> {
    Logger.info('处理代付通知', { channel, data });

    try {
      // Verify notification signature
      const isValid = paymentService.verifyNotification(channel, data);
      if (!isValid) {
        throw new AppError('代付通知签名验证失败', ErrorCode.PAYMENT_NOTIFICATION_INVALID);
      }

      // Parse notification data
      const notification: PaymentNotification | null = paymentService.parseNotification(channel, data);
      if (!notification) {
        throw new AppError('代付通知数据解析失败', ErrorCode.PAYMENT_NOTIFICATION_PARSE_ERROR);
      }

      // Find payout record by order ID
      const payoutRecord = await prisma.payoutRecord.findUnique({
        where: { orderId: notification.orderId },
      });

      if (!payoutRecord) {
        Logger.warn('未找到对应的代付记录', { orderId: notification.orderId });
        throw new AppError('代付记录不存在', ErrorCode.ORDER_NOT_FOUND);
      }

      // Determine final status based on notification status
      let finalStatus: PayoutStatus;
      switch (notification.status) {
        case 'SUCCESS':
        case 'TRANSFER_SUCCESS':
          finalStatus = PayoutStatus.COMPLETED;
          break;
        case 'PAY_ERROR':
        case 'TRANSFER_FAILED':
        case 'PAY_CANCEL':
        case 'TRANSFER_CLOSED':
          finalStatus = PayoutStatus.FAILED;
          break;
        default:
          Logger.info('代付通知状态未变更', { 
            orderId: notification.orderId, 
            status: notification.status 
          });
          return; // No status change needed
      }

      // Update payout record status
      await prisma.payoutRecord.update({
        where: { orderId: notification.orderId },
        data: {
          status: finalStatus,
          platOrderNum: notification.platOrderNum || payoutRecord.platOrderNum,
          paymentResponse: JSON.stringify(notification.rawData),
        },
      });

      Logger.info('代付通知处理完成', {
        orderId: notification.orderId,
        finalStatus,
        platOrderNum: notification.platOrderNum
      });

    } catch (error) {
      Logger.error('代付通知处理失败', error, { channel, data });
      throw error;
    }
  }
}

// Create payment service instance
export const payoutService = new PayoutService();
